//
// C++ Implementation: Query
//
// Description:
//
//
// Author: Kristina Bennett <kls93@caip.rutgers.edu>, (C) 2008
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include "Query.h"
#include <algorithm>
#include <cmath>
#include <fstream>

#if DEBUG_LEVEL > 0
#include <iostream>
using std::cout;
using std::endl;
#endif


/**
 * If you have four cards, get all four hand scores.
 * Returns a 4-element vector. Each value is the score
 * of the hand *without* the card at that index.
 */
vector<int> Query::FourCardValues(const CCardStack& hand) {
  if (hand.Size() != 4) {
    return vector<int>(4,0);
  }
  CCardStack subhand;
  vector<int> scores(4);
  for (int i=1; i<4; i++) {
    subhand.Push(hand.GetCard(i));
  }
  scores[0] = subhand.Value();

  int i=0;
  while (i<3) {
    subhand.RemoveCard(0);
    subhand.Push(hand.GetCard(i));
    i++;
    scores[i] = subhand.Value();
  }

  return scores;
}

CCardStack Query::maxValueCards(const CCardStack& pack, eSuit suit) {
    CCardStack ret;
    int maxValue = 0;
    for (CONST_VI ci = pack.begin(); ci != pack.end(); ci++) {
        if (ci->Suit() == suit) {
            int cardValue = ci->Value();
            if (cardValue == maxValue) {
                ret.Push(*ci);
            }
            else if (cardValue > maxValue) {
                ret.Clear();
                ret.Push(*ci);
                maxValue = cardValue;
            }
        }
    }
    return ret;
}

CCardStack Query::minValueCards(const CCardStack& pack, eSuit suit) {
    CCardStack ret;
    int minValue = 53;
    for (CONST_VI ci = pack.begin(); ci != pack.end(); ci++) {
        if (ci->Suit() == suit) {
            int cardValue = ci->Value();
            if (cardValue == minValue) {
                ret.Push(*ci);
            }
            else if (cardValue < minValue) {
                ret.Clear();
                ret.Push(*ci);
                minValue = cardValue;
            }
        }
    }
    return ret;
}

float Query::expectedValue(const CCardStack& pack, eSuit suit) {
    int valueSum = 0;
    int count = 0;
    for (CONST_VI ci = pack.begin(); ci != pack.end(); ci++) {
        if (ci->Suit() == suit) {
            valueSum += ci->Value();
            count++;
        }
    }
    return (float) valueSum / count;
}

int Query::gainFromCard(CCardStack& hand, const CCard& card, int discard) {
    int baseValue = hand.Value();
    hand.Push(card);
    int newValue = hand.Value(discard);
    hand.Pop();
    return (newValue - baseValue);
}

int Query::maxGainFromCard(CCardStack& hand, const CCard& card) {
    int baseValue = hand.Value();
    hand.Push(CCard(card.GetIndex()));
    int maxValue;
    if (hand.Size() == 4) {
        maxValue = Query::maxValue(Query::FourCardValues(hand));
    }
    else {
        maxValue = hand.Value();
    }
    hand.Pop();
    return (maxValue - baseValue);
}

int Query::RandomChoice(int numChoices) {
  return rand() % numChoices;
}

int Query::maxValue(const vector<int>& values) {
  return *(std::max_element(values.begin(),values.end()));
}

int Query::maxValueIndex(const vector<int>& values) {
    int ret = -1;
    if (values.size() > 0) {
        ret = (std::max_element(values.begin(), values.end())) - values.begin();
    }
    return ret;
}

int Query::maxValue(const int* array, int length) {
    int max = array[0];
    for (int i = 1; i < length; i++) {
        if (array[i] > max) {
            max = array[i];
        }
    }
    return max;
}

int Query::maxValueIndex(const int* array, int length) {
    int max = array[0];
    int ret = 0;
    for (int i = 1; i < length; i++) {
        if (array[i] > max) {
            max = array[i];
            ret = i;
        }
    }
    return ret;
}

int Query::minValue(const int* array, int length) {
    int min = array[0];
    for (int i = 1; i < length; i++) {
        if (array[i] < min) {
            min = array[i];
        }
    }
    return min;
}

int Query::minValueIndex(const int* array, int length) {
    int min = array[0];
    int ret = 0;
    for (int i = 1; i < length; i++) {
        if (array[i] < min) {
            min = array[i];
            ret = i;
        }
    }
    return ret;
}

CCard Query::minValueCard(const CCardStack& pack) {
    int minPos = minValueIndex(pack);
    if (minPos >= 0) {
        return CCard(pack.at(minPos).GetIndex());
    }
    else {
        return CCard(INVALID_CARD);
    }
}

int Query::minValueIndex(const CCardStack& cards) {
  if (cards.Size())
    return (std::min_element(cards.begin(),cards.end(),lessThan) -
            cards.begin());
  else
    return -1;

//   // Hand-coded method, for testing
//   if (cards.Size()) {
//     int minValue = cards.GetCard(0).Value();
//     int minIndex = 0;
//     for (int i=1; i<cards.Size(); i++) {
//       int value = cards.GetCard(i).Value();
//       if (value < minValue) {
//         minValue = value;
//         minIndex = i;
//       }
//     }
// #if DEBUG_LEVEL > 1
//     cout << "The plain method says " << minIndex << ". \n";
//     cout.flush();
// #endif
//     return minIndex;
//   }
//   else {
// #if DEBUG_LEVEL > 1
//     cout << "There are no cards in the stack.\n";
//     cout.flush();
// #endif
//     return -1;
//   }
}

bool Query::lessThan(const CCard& l, const CCard& r) {
  return (l.Value() < r.Value());
}

bool Query::evalConjunction(bool* decisions, int numDecisions) {
    bool ret = true;
    for (int i = 0; i < numDecisions; i++) {
        ret = ret && decisions[i];
        if (ret == false) {
            break;
        }
    }
    return ret;
}

int Query::majority(int* pickedCardArray, int arraySize) {
    int equivalenceClass[arraySize];
    for (int i = 0; i < arraySize; i++) {
        equivalenceClass[i] = 1;
    }
    int maxClassSize = 1;
    int maxClass = 0;
    for (int i = 0; i < arraySize; i++) {
        if (equivalenceClass[i] == 1) {
            for (int j = i+1; j < arraySize; j++) {
                if (equivalenceClass[j] == 1) {
                    if (pickedCardArray[j] == pickedCardArray[i]) {
                        equivalenceClass[i]++;
                        equivalenceClass[j]++;
                        if (equivalenceClass[i] > maxClassSize) {
                            maxClass = i;
                            maxClassSize = equivalenceClass[i];
                        }
                    }
                }
            }
        }
    }
    return pickedCardArray[maxClass];
}

bool Query::isValid(const Move& move) {
    return !(move.knock == false && move.cardPicked.IsJoker() && move.cardDrawn.IsJoker());
}

int Query::factorial(int x) {
    int ret = 1;
    for (int i = 2; i <= x; i++) {
        ret *= i;
    }
    return ret;
}

int Query::combinations(int n, int k) {
    int ret = n;
    int limit = n - k;
    for (int i = n-1; i > limit; i--) {
        ret *= i;
    }
    return ret;
}

int Query::choose(int n, int k) {
    return combinations(n, k) / factorial(k);
}

void Query::debugOut(const string& line, int debugLevel) {
#if DEBUG_LEVEL > 0
    if (debugLevel <= DEBUG_LEVEL) {
        cout<< line;
        cout.flush();
    }
#endif
}

void Query::debugValue(int value, int debugLevel) {
#if DEBUG_LEVEL > 0
    if (debugLevel <= DEBUG_LEVEL) {
        cout<< value << endl;
        cout.flush();
    }
#endif
}
